package ws.cmpe451.group7.ourcity.webservice.user;

import java.io.IOException;
import java.io.PrintWriter;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;

import ws.cmpe451.group7.ourcity.webservice.Callable;
import ws.cmpe451.group7.ourcity.webservice.ControllerWrapper;
import ws.cmpe451.group7.ourcity.webservice.exception.GeneralIllegalArgumentException;
import cmpe451.group7.ourcity.dao.CommentDAO;
import cmpe451.group7.ourcity.dao.UserDAO;
import cmpe451.group7.ourcity.dao.impl.UserDAOImpl;
import cmpe451.group7.ourcity.entity.Badge;
import cmpe451.group7.ourcity.entity.Comment;
import cmpe451.group7.ourcity.entity.Place;
import cmpe451.group7.ourcity.entity.User;
import cmpe451.group7.ourcity.service.UserService;
import cmpe451.group7.ourcity.util.Aes;
import cmpe451.group7.ourcity.util.GlobalVariables;
import cmpe451.group7.ourcity.util.JSONUtils;
import cmpe451.group7.ourcity.util.Utils;

/**
 * Controller for user related tags
 */
@Controller
public class UserController {

	@Autowired
	private UserService userService;

	@Autowired
	private UserDAO userDAO;

	@Autowired
	private CommentDAO commentDAO;

	/**
	 * Used for authenticating a user
	 * 
	 * Parameters: email & password
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	@Transactional
	@RequestMapping("/webservice/user/login")
	protected void login(HttpServletRequest request,
			HttpServletResponse response) throws IOException {
		/*
		 * This call returns a JSON object with the result set coming from DAO
		 * object
		 */
		response.setContentType("application/json");
		PrintWriter responseWriter = response.getWriter();
		Map<String, String[]> parameters = request.getParameterMap();
		Set<String> keys = parameters.keySet();

		try {
			if (keys.size() == 0) {
				throw new GeneralIllegalArgumentException("Unsupported action");
			} else {
				String email = "";
				String password = "";
				for (String arg : keys) {
					switch (arg) {
					case "email":
						email = parameters.get(arg)[0];
						break;
					case "password":
						password = Aes.decryptPassword(parameters.get(arg)[0]);
						if (password == null) {
							throw new GeneralIllegalArgumentException(
									"Malformed password");
						}
						break;
					default:
						throw new GeneralIllegalArgumentException(
								"Unsupported argument : " + arg);
					}
				}

				User user = userService.authenticate(email, password);

				if (user != null) {
					JSONUtils.JSONEntityWriter(user, responseWriter);
				} else {
					throw new GeneralIllegalArgumentException(
							"Wrong username or password");
				}
			}
		} catch (GeneralIllegalArgumentException e) {
			// request contains illegal parameter
			responseWriter.printf("{\"success\":0, \"error\":\"%s\"}",
					e.getMessage());
		} catch (InvalidKeyException | NoSuchAlgorithmException
				| NoSuchPaddingException | IllegalBlockSizeException
				| BadPaddingException | InvalidKeySpecException | JSONException e) {
			// TODO Fix this stack of unhandled exceptions ?
			responseWriter
					.print("{\"success\":0, \"error\":\"Servlet error\"}");
			e.printStackTrace();
		}

		responseWriter.close();
	}

	/**
	 * used for registering a user to the system
	 * 
	 * parameters: name, surname, email, password
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	@Transactional
	@RequestMapping("/webservice/user/register")
	protected void register(HttpServletRequest request,
			HttpServletResponse response) throws IOException {
		/*
		 * This call returns a JSON object with the result set coming from DAO
		 * object
		 */
		response.setContentType("application/json");
		PrintWriter responseWriter = response.getWriter();
		Map<String, String[]> parameters = request.getParameterMap();
		Set<String> keys = parameters.keySet();

		try {
			if (keys.size() == 0) {
				throw new GeneralIllegalArgumentException("Unsupported action");
			} else {
				String name = "";
				String surname = "";
				String email = "";
				String password = "";
				for (String arg : keys) {
					switch (arg) {
					case "name":
						name = parameters.get(arg)[0];
						break;
					case "surname":
						surname = parameters.get(arg)[0];
						break;
					case "email":
						email = parameters.get(arg)[0];
						break;
					case "password":
						password = Aes.decryptPassword(parameters.get(arg)[0]);
						if (password == null) {
							throw new GeneralIllegalArgumentException(
									"Malformed password");
						}
						break;
					default:
						throw new GeneralIllegalArgumentException(
								"Unsupported argument : " + arg);
					}
				}

				User user = userService.authenticate(email, password);

				if (user != null) {
					JSONUtils.JSONEntityWriter(user, responseWriter);
				} else {
					userDAO.insertNewUser(name, surname, email, password);
					User u = userService.authenticate(email, password);
					JSONUtils.JSONEntityWriter(u, responseWriter);
				}
			}
		} catch (GeneralIllegalArgumentException e) {
			// request contains illegal parameter
			responseWriter.printf("{\"success\":0, \"error\":\"%s\"}",
					e.getMessage());
		} catch (InvalidKeyException | NoSuchAlgorithmException
				| NoSuchPaddingException | IllegalBlockSizeException
				| BadPaddingException | InvalidKeySpecException | JSONException e) {
			// TODO Fix this stack of unhandled exceptions ?
			responseWriter
					.print("{\"success\":0, \"error\":\"Servlet error\"}");
			e.printStackTrace();
		}

		responseWriter.close();
	}

	/**
	 * Get the comments of the user
	 * 
	 * parameters: userID
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	@Transactional
	@RequestMapping("/webservice/user/getComments")
	public void getComments(HttpServletRequest request,
			HttpServletResponse response) throws IOException {

		Callable func = new Callable() {

			@Override
			public void call(HttpServletRequest request,
					HttpServletResponse response, PrintWriter responseWriter,
					Map<String, String[]> parameters) throws JSONException {

				Long userID = Utils.parseLongFromMap(parameters, "userID");
				List<Comment> comments = commentDAO.findUserComments(userID);

				JSONUtils.JSONListWriter(comments, responseWriter);
			}

		};

		ControllerWrapper.wrapMethod(request, response, func,
				GlobalVariables.getUserCommentRequiredParameters);
	}

	/**
	 * returns the liked places of the user
	 * 
	 * parameters: userID
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	@Transactional
	@RequestMapping("/webservice/user/getLikedPlaces")
	public void getLikedPlaces(HttpServletRequest request,
			HttpServletResponse response) throws IOException {

		Callable func = new Callable() {

			@Override
			public void call(HttpServletRequest request,
					HttpServletResponse response, PrintWriter responseWriter,
					Map<String, String[]> parameters)
					throws GeneralIllegalArgumentException, JSONException {

				long userId = Utils.parseLongFromMap(parameters, "userID");
				User user = userDAO.findByID(userId);
				if (user == null)
					throw new GeneralIllegalArgumentException(
							"User with the given id does not exist");

				Set<Place> likedPlaces = user.getLikedPlace();
				if (likedPlaces == null)
					throw new GeneralIllegalArgumentException(
							"Error occured while getting the liked places of the user");

				JSONUtils.JSONListWriter(new ArrayList<Place>(likedPlaces),
						responseWriter);
			}

		};

		ControllerWrapper.wrapMethod(request, response, func,
				GlobalVariables.getUserLikedPlacesRequiredParameters);
	}

	/**
	 * Returns the badges of the user
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	@Transactional
	@RequestMapping("/webservice/user/getBadges")
	public void getBadges(HttpServletRequest request,
			HttpServletResponse response) throws IOException {

		Callable func = new Callable() {

			@Override
			public void call(HttpServletRequest request,
					HttpServletResponse response, PrintWriter responseWriter,
					Map<String, String[]> parameters)
					throws GeneralIllegalArgumentException, JSONException {

				long userId = Utils.parseLongFromMap(parameters, "userID");
				User user = userDAO.findByID(userId);
				if (user == null)
					throw new GeneralIllegalArgumentException(
							"User with the given id does not exist");

				Set<Badge> badges = user.getBadges();
				if (badges == null)
					throw new GeneralIllegalArgumentException(
							"Error occured while getting the badges of the user");

				JSONUtils.JSONListWriter(new ArrayList<Badge>(badges),
						responseWriter);
			}

		};

		ControllerWrapper.wrapMethod(request, response, func,
				GlobalVariables.getBadgesRequiredParameters);
	}
}